package com.sjtu.rental.services;

import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mysql.jdbc.StringUtils;
import com.sjtu.rental.common.Constant;
import com.sjtu.rental.common.PaginationSupport;
import com.sjtu.rental.entity.House;
import com.sjtu.rental.entity.HouseFile;
import com.sjtu.rental.entity.RentRecord;
import com.sjtu.rental.entity.ShowingRecord;
import com.sjtu.rental.entity.UserInfo;
import com.sjtu.rental.form.SearchForm;

@Service
public class HouseService {

	@Autowired
	private GenericDao genericDao;

	@SuppressWarnings("unchecked")
	@Transactional
	public void saveOrUpdate(House house, String[] files) {
		this.genericDao.getHibernateTemplate().saveOrUpdate(house);
		final long houseId = house.getId();
		this.genericDao.getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session s) throws HibernateException,
					SQLException {
				s.createQuery("delete HouseFile f where f.houseId = ?")
						.setLong(0, houseId).executeUpdate();
				return null;
			}
		});
		if (files != null) {
			for (String string : files) {
				this.genericDao.getHibernateTemplate().save(
						new HouseFile(houseId, string));
			}
		}
	}

	@Transactional
	public PaginationSupport getHousesByUserId(Long userId,
			Integer approveState, int page) {
		DetachedCriteria crit = DetachedCriteria.forClass(House.class);
		if (userId != null) {
			crit.add(Restrictions.eq("creator", userId));
		}
		crit.add(Restrictions.eq("isDeleted", 0));
		if (approveState != null) {
			crit.add(Restrictions.eq("approveState", approveState));
		}
		crit.addOrder(Order.desc("createDate"));
		return genericDao.findByCriteria(crit, page);
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public House getById(Long id) {
		House house = genericDao.getHibernateTemplate().get(House.class, id);
		List<HouseFile> files = genericDao.getHibernateTemplate()
				.findByCriteria(
						DetachedCriteria.forClass(HouseFile.class).add(
								Restrictions.eq("houseId", id)));
		house.setHouseFiles(files);
		return house;
	}

	@Transactional
	public int getCountByUserId(Long userId, Integer approveState) {
		DetachedCriteria crit = DetachedCriteria.forClass(House.class).add(
				Restrictions.eq("isDeleted", 0)).add(
				Restrictions.eq("creator", userId));
		if (approveState != null) {
			crit.add(Restrictions.eq("approveState", approveState));
		}
		crit.setProjection(Projections.count("id"));
		return (Integer) genericDao.getHibernateTemplate().findByCriteria(crit)
				.get(0);
	}

	@Transactional
	public int getAppointmentCountByUserId(Long userId) {
		DetachedCriteria crit = DetachedCriteria.forClass(ShowingRecord.class)
				.add(Restrictions.eq("uiId", userId));
		crit.setProjection(Projections.count("id"));
		return (Integer) genericDao.getHibernateTemplate().findByCriteria(crit)
				.get(0);
	}

	@Transactional
	@SuppressWarnings("unchecked")
	public List<House> getHouseByPrice() {
		DetachedCriteria crit = DetachedCriteria.forClass(House.class).add(
				Restrictions.eq("isDeleted", 0)).add(
				Restrictions.eq("approveState", Constant.HOUSE_STATE_PAID));
		crit.addOrder(Order.desc("price"));
		return genericDao.getHibernateTemplate().findByCriteria(crit, 0, 30);
	}

	@Transactional
	@SuppressWarnings("unchecked")
	public List<House> getHouseByTime() {
		DetachedCriteria crit = DetachedCriteria.forClass(House.class).add(
				Restrictions.eq("isDeleted", 0)).add(
				Restrictions.eq("approveState", Constant.HOUSE_STATE_PAID));
		crit.addOrder(Order.desc("createDate"));
		return genericDao.getHibernateTemplate().findByCriteria(crit, 0, 15);
	}

	@Transactional
	@SuppressWarnings("unchecked")
	public List<House> getDealHouses() {
		DetachedCriteria crit = DetachedCriteria.forClass(House.class).add(
				Restrictions.eq("isDeleted", 0)).add(
				Restrictions.eq("approveState", Constant.HOUSE_STATE_RENTED));
		crit.addOrder(Order.desc("createDate"));
		return genericDao.getHibernateTemplate().findByCriteria(crit, 0, 10);
	}

	@Transactional
	public boolean reserve(Long id, Long uid, String showingDate,
			String contactTime, String remark) throws ParseException {
		// 是否有人预约过
		DetachedCriteria crit = DetachedCriteria.forClass(ShowingRecord.class);
		crit.add(Restrictions.eq("houseId", id))
				.add(
						Restrictions.eq("showingDate", showingDate.replaceAll(
								"-", "")));
		boolean free = genericDao.getHibernateTemplate().findByCriteria(crit)
				.isEmpty();
		if (!free) {
			// 预约过
			return false;
		} else {
			ShowingRecord sr = new ShowingRecord(id, uid, showingDate
					.replaceAll("-", ""), contactTime, remark,
					ShowingRecord.REQUEST_APPOINTMENT);
			genericDao.getHibernateTemplate().save(sr);
			return true;
		}
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public List<House> getHotHouses() {
		String hql = "SELECT count(t1.id),t1.houseId FROM ShowingRecord t1, House t2 where t1.houseId = t2.id"
				+ " and t2.isDeleted = 0 and t2.approveState = ? group by t1.houseId order by count(t1.id) desc";
		Query q = genericDao.getHibernateTemplate().getSessionFactory()
				.getCurrentSession().createQuery(hql);
		List<Object[]> result = q.setLong(0, Constant.HOUSE_STATE_PAID)
				.setMaxResults(11).list();
		List<House> houses = new ArrayList<House>();
		for (Object[] objects : result) {
			Long houseId = (Long) objects[1];
			House house = genericDao.getHibernateTemplate().get(House.class,
					houseId);
			house.setAppointmentCount((Long) objects[0]);
			houses.add(house);
		}
		return houses;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public int getTodayHouseCount() {
		DetachedCriteria crit = DetachedCriteria.forClass(House.class).add(
				Restrictions.eq("createDate", new Date())).add(
				Restrictions.eq("isDeleted", 0)).setProjection(
				Projections.count("id"));
		List l = genericDao.getHibernateTemplate().findByCriteria(crit);
		return ((Number) l.get(0)).intValue();
	}

	@Transactional
	public PaginationSupport getHouseBySearchForm(SearchForm searchForm,
			int page) {
		DetachedCriteria crit = DetachedCriteria.forClass(House.class);
		crit.add(Restrictions.eq("isDeleted", 0));
		crit.add(Restrictions.eq("approveState", Constant.HOUSE_STATE_PAID));
		if (!StringUtils.isNullOrEmpty(searchForm.getDistrict())) {
			crit.add(Restrictions.eq("district", Integer.parseInt(searchForm
					.getDistrict())));
		}
		if (!StringUtils.isNullOrEmpty(searchForm.getArea())) {
			String[] scope = searchForm.getArea().split(",");
			crit.add(Restrictions.ge("area", Double.valueOf(scope[0])));
			if (!StringUtils.isNullOrEmpty(scope[1])) {
				crit.add(Restrictions.le("area", Double.valueOf(scope[1])));
			}
		}
		if (!StringUtils.isNullOrEmpty(searchForm.getPrice())) {
			String[] scope = searchForm.getPrice().split(",");
			crit.add(Restrictions.ge("price", Double.valueOf(scope[0])));
			if (!StringUtils.isNullOrEmpty(scope[1])) {
				crit.add(Restrictions.le("price", Double.valueOf(scope[1])));
			}
		}
		if (!StringUtils.isNullOrEmpty(searchForm.getRoom())) {
			if (searchForm.getRoom().equals(">5")) {
				crit
						.add(Restrictions
								.sqlRestriction("substring_index(house_type,'-',1) > 5"));
			} else {
				crit.add(Restrictions.like("houseType", searchForm.getRoom()
						+ "-%"));
			}
		}
		if (!StringUtils.isNullOrEmpty(searchForm.getName())) {
			crit.add(Restrictions.or(Restrictions.like("housingName", "%"
					+ searchForm.getName() + "%"), Restrictions.like("address",
					"%" + searchForm.getName() + "%")));
		}
		return genericDao.findByCriteria(crit, page);
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public PaginationSupport getAppointmentHouses(Long uiId, Integer page) {
		DetachedCriteria crit = DetachedCriteria.forClass(ShowingRecord.class);
		crit.add(Restrictions.eq("uiId", uiId));
		crit.addOrder(Order.desc("showingDate"));
		PaginationSupport p = genericDao.findByCriteria(crit, page);
		List<ShowingRecord> rs = (List<ShowingRecord>) p.getItems();
		for (ShowingRecord r : rs) {
			r.setHouse(genericDao.getHibernateTemplate().get(House.class,
					r.getHouseId()));
		}
		return p;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public PaginationSupport getAppointments(Long id, Integer page) {
		DetachedCriteria crit = DetachedCriteria.forClass(ShowingRecord.class);
		crit.add(Restrictions.eq("houseId", id));
		crit.addOrder(Order.desc("id"));
		PaginationSupport p = genericDao.findByCriteria(crit, page);
		List<ShowingRecord> rs = (List<ShowingRecord>) p.getItems();
		for (ShowingRecord r : rs) {
			r.setUser(genericDao.getHibernateTemplate().get(UserInfo.class,
					r.getUiId()));
			r.setHouse(genericDao.getHibernateTemplate().get(House.class,
					r.getHouseId()));
		}
		return p;
	}

	@Transactional
	public void changeAppointmentState(Long id, int state) {
		ShowingRecord r = genericDao.getHibernateTemplate().get(
				ShowingRecord.class, id);
		r.setState(state);
		genericDao.getHibernateTemplate().update(r);
	}

	@Transactional
	public void rentHouse(Long appointmentId) {
		ShowingRecord r = genericDao.getHibernateTemplate().get(
				ShowingRecord.class, appointmentId);
		Long houseId = r.getHouseId();
		House house = genericDao.getHibernateTemplate().get(House.class,
				houseId);
		if (house.getIsDeleted() == 0
				&& Constant.HOUSE_STATE_PAID.equals(house.getApproveState())) {
			house.setApproveState(Constant.HOUSE_STATE_RENTED);
			RentRecord rentRecord = new RentRecord();
			rentRecord.setAppointmentId(appointmentId);
			rentRecord.setCheckIn(new Date());
			rentRecord.setHouseId(houseId);
			rentRecord.setUserId(r.getUiId());
			rentRecord.setPrice(house.getPrice());
			genericDao.getHibernateTemplate().save(rentRecord);
		}
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public PaginationSupport getOrders(Long id, int page) {
		DetachedCriteria crit = DetachedCriteria.forClass(RentRecord.class);
		crit.add(Restrictions.eq("houseId", id));
		crit.addOrder(Order.desc("checkIn"));
		PaginationSupport p = genericDao.findByCriteria(crit, page);
		List<RentRecord> rs = (List<RentRecord>) p.getItems();
		for (RentRecord r : rs) {
			r.setUser(genericDao.getHibernateTemplate().get(UserInfo.class,
					r.getUserId()));
			r.setHouse(genericDao.getHibernateTemplate().get(House.class,
					r.getHouseId()));
		}
		return p;
	}

	@Transactional
	public void checkoutHouse(Long id) {
		RentRecord record = genericDao.getHibernateTemplate().get(
				RentRecord.class, id);
		record.setCheckOut(new Date());
		House house = genericDao.getHibernateTemplate().get(House.class,
				record.getHouseId());
		house.setApproveState(Constant.HOUSE_STATE_UNCONFIRM);
		genericDao.getHibernateTemplate().update(house);
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public PaginationSupport getOrderHouses(Long uiId, int page) {
		DetachedCriteria crit = DetachedCriteria.forClass(RentRecord.class);
		crit.add(Restrictions.eq("userId", uiId));
		crit.addOrder(Order.desc("checkIn"));
		PaginationSupport p = genericDao.findByCriteria(crit, page);
		List<RentRecord> rs = (List<RentRecord>) p.getItems();
		for (RentRecord r : rs) {
			r.setHouse(genericDao.getHibernateTemplate().get(House.class,
					r.getHouseId()));
		}
		return p;
	}

	@Transactional
	public void rating(long id, String marks, String remark) {
		RentRecord r = genericDao.getHibernateTemplate().get(RentRecord.class,
				id);
		if (!StringUtils.isNullOrEmpty(marks)) {
			r.setMarks(Integer.parseInt(marks));
		}
		r.setRemark(remark);
		genericDao.getHibernateTemplate().update(r);
	}

	@Transactional
	public void changeHouseState(Long id, Integer state) {
		House house = genericDao.getHibernateTemplate().get(House.class, id);
		house.setApproveState(state);
		genericDao.getHibernateTemplate().update(house);
	}
}
